"""
评价指标计算模块
提供全面的回归评价指标
"""
import numpy as np
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
from scipy.stats import pearsonr, spearmanr


class MetricsCalculator:
    """
    全面的回归评价指标计算器
    
    支持的指标:
        - MAE: 平均绝对误差
        - RMSE: 均方根误差
        - R²: 决定系数
        - MAPE: 平均绝对百分比误差
        - MedAE: 中位数绝对误差
        - MaxError: 最大误差
        - MSLE: 均方对数误差
        - Pearson: Pearson相关系数
        - Spearman: Spearman相关系数
        - Accuracy@X%: X%误差范围内的准确率
    """
    
    def __init__(self):
        self.metric_descriptions = {
            'MAE': '平均绝对误差 - 预测值与真实值的平均绝对差值',
            'RMSE': '均方根误差 - 对大误差更敏感，常用于回归任务',
            'R²': '决定系数 - 模型解释数据变异的能力 (越接近1越好)',
            'MAPE': '平均绝对百分比误差 - 相对误差，适合不同量级的比较',
            'MedAE': '中位数绝对误差 - 对异常值更鲁棒',
            'MaxError': '最大误差 - 最坏情况下的预测误差',
            'MSLE': '均方对数误差 - 对预测不足的惩罚小于预测过度',
            'Pearson': 'Pearson相关系数 - 线性相关程度 (-1到1)',
            'Spearman': 'Spearman相关系数 - 单调关系的强度',
            'Accuracy@10%': '10%误差范围内的准确率',
            'Accuracy@20%': '20%误差范围内的准确率'
        }
    
    def calculate_all_metrics(self, y_true, y_pred):
        """
        计算所有回归评价指标
        
        参数:
            y_true: 真实值数组
            y_pred: 预测值数组
        
        返回:
            metrics: 字典，包含所有指标
        """
        y_true = np.array(y_true).flatten()
        y_pred = np.array(y_pred).flatten()
        
        # 确保数据有效
        if len(y_true) == 0 or len(y_pred) == 0:
            return self._empty_metrics()
        
        metrics = {}
        
        # 1. MAE - 平均绝对误差
        metrics['MAE'] = mean_absolute_error(y_true, y_pred)
        
        # 2. RMSE - 均方根误差
        metrics['RMSE'] = np.sqrt(mean_squared_error(y_true, y_pred))
        
        # 3. R² Score - 决定系数
        try:
            metrics['R²'] = r2_score(y_true, y_pred)
        except:
            metrics['R²'] = 0.0
        
        # 4. MAPE - 平均绝对百分比误差 (避免除零)
        mask = y_true != 0
        if mask.sum() > 0:
            mape_values = np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])
            metrics['MAPE'] = np.mean(mape_values) * 100
        else:
            metrics['MAPE'] = 0.0
        
        # 5. MedAE - 中位数绝对误差
        metrics['MedAE'] = np.median(np.abs(y_true - y_pred))
        
        # 6. Max Error - 最大误差
        metrics['MaxError'] = np.max(np.abs(y_true - y_pred))
        
        # 7. MSLE - 均方对数误差
        try:
            y_true_log = np.log1p(np.maximum(y_true, 0))
            y_pred_log = np.log1p(np.maximum(y_pred, 0))
            metrics['MSLE'] = np.mean((y_true_log - y_pred_log) ** 2)
        except:
            metrics['MSLE'] = 0.0
        
        # 8. Pearson相关系数
        try:
            if len(y_true) > 1 and np.std(y_true) > 0 and np.std(y_pred) > 0:
                metrics['Pearson'], _ = pearsonr(y_true, y_pred)
            else:
                metrics['Pearson'] = 0.0
        except:
            metrics['Pearson'] = 0.0
        
        # 9. Spearman相关系数
        try:
            if len(y_true) > 1:
                metrics['Spearman'], _ = spearmanr(y_true, y_pred)
            else:
                metrics['Spearman'] = 0.0
        except:
            metrics['Spearman'] = 0.0
        
        # 10. 准确率指标 - 在x%误差范围内的样本比例
        relative_errors = np.abs((y_true - y_pred) / (y_true + 1e-8))
        metrics['Accuracy@10%'] = (relative_errors <= 0.1).mean() * 100
        metrics['Accuracy@20%'] = (relative_errors <= 0.2).mean() * 100
        
        # 11. 标准差
        metrics['Std_Error'] = np.std(y_true - y_pred)
        
        return metrics
    
    def _empty_metrics(self):
        """返回空指标字典"""
        return {
            'MAE': 0.0,
            'RMSE': 0.0,
            'R²': 0.0,
            'MAPE': 0.0,
            'MedAE': 0.0,
            'MaxError': 0.0,
            'MSLE': 0.0,
            'Pearson': 0.0,
            'Spearman': 0.0,
            'Accuracy@10%': 0.0,
            'Accuracy@20%': 0.0,
            'Std_Error': 0.0
        }
    
    def print_metrics(self, metrics, title="Evaluation Metrics"):
        """
        格式化打印所有指标
        
        参数:
            metrics: 指标字典
            title: 标题
        """
        print(f"\n{'='*70}")
        print(f"{title:^70}")
        print(f"{'='*70}")
        
        # 核心误差指标
        print("\n【Core Error Metrics】")
        print(f"  MAE (Mean Absolute Error):        {metrics['MAE']:.6f}")
        print(f"  RMSE (Root Mean Squared Error):   {metrics['RMSE']:.6f}")
        print(f"  MedAE (Median Absolute Error):    {metrics['MedAE']:.6f}")
        print(f"  MaxError (Maximum Error):         {metrics['MaxError']:.6f}")
        print(f"  Std_Error (Error Std Dev):        {metrics['Std_Error']:.6f}")
        
        # 相对误差指标
        print("\n【Relative Error Metrics】")
        print(f"  MAPE (Mean Absolute % Error):     {metrics['MAPE']:.2f}%")
        print(f"  MSLE (Mean Squared Log Error):    {metrics['MSLE']:.6f}")
        
        # 模型质量指标
        print("\n【Model Quality Metrics】")
        print(f"  R² (Coefficient of Determination): {metrics['R²']:.6f}")
        print(f"  Pearson Correlation:               {metrics['Pearson']:.6f}")
        print(f"  Spearman Correlation:              {metrics['Spearman']:.6f}")
        
        # 准确率指标
        print("\n【Accuracy Metrics】")
        print(f"  Accuracy within 10% error:        {metrics['Accuracy@10%']:.2f}%")
        print(f"  Accuracy within 20% error:        {metrics['Accuracy@20%']:.2f}%")
        
        print(f"{'='*70}\n")
    
    def get_metric_description(self, metric_name):
        """
        获取指标说明
        
        参数:
            metric_name: 指标名称
        
        返回:
            description: 指标说明
        """
        return self.metric_descriptions.get(metric_name, "Unknown metric")
    
    def print_metric_explanations(self):
        """打印所有指标的详细说明"""
        print("\n" + "="*70)
        print("Evaluation Metrics Explanation".center(70))
        print("="*70)
        
        explanations = {
            '【Core Error Metrics】': {
                'MAE': {
                    'Definition': '预测值与真实值的平均绝对差值',
                    'Usage': '直观反映预测误差的大小，单位与目标变量相同',
                    'Advantage': '易于理解，对异常值不敏感',
                    'Best For': '当所有误差同等重要时使用'
                },
                'RMSE': {
                    'Definition': '预测误差平方的平均值再开方',
                    'Usage': '对大误差给予更高惩罚',
                    'Advantage': '更关注大误差，常用于模型优化',
                    'Best For': '当大误差代价高时使用'
                },
                'MedAE': {
                    'Definition': '所有绝对误差的中位数',
                    'Usage': '反映中等水平的预测误差',
                    'Advantage': '对极端异常值非常鲁棒',
                    'Best For': '数据存在异常值时使用'
                },
                'MaxError': {
                    'Definition': '最大的单个预测误差',
                    'Usage': '反映最坏情况下的性能',
                    'Advantage': '关注边界情况',
                    'Best For': '需要保证最坏情况性能时'
                }
            },
            '【Relative Error Metrics】': {
                'MAPE': {
                    'Definition': '平均绝对百分比误差',
                    'Usage': '标准化的误差，便于不同量级数据的比较',
                    'Advantage': '无单位，易于跨数据集比较',
                    'Best For': '目标值变化范围大时使用'
                },
                'MSLE': {
                    'Definition': '对数空间的均方误差',
                    'Usage': '对预测不足的惩罚小于预测过度',
                    'Advantage': '适合目标值跨越多个数量级',
                    'Best For': '偏向低估比高估更可接受的场景'
                }
            },
            '【Model Quality Metrics】': {
                'R²': {
                    'Definition': '模型解释的方差占总方差的比例',
                    'Usage': '衡量模型拟合优度 (0-1之间，越接近1越好)',
                    'Advantage': '直观反映模型整体性能',
                    'Best For': '评估模型的解释能力'
                },
                'Pearson': {
                    'Definition': '预测值与真实值的线性相关系数',
                    'Usage': '衡量线性关系强度 (-1到1)',
                    'Advantage': '反映线性相关性',
                    'Best For': '检验线性关系'
                },
                'Spearman': {
                    'Definition': '基于排序的相关系数',
                    'Usage': '衡量单调关系强度',
                    'Advantage': '对非线性单调关系也有效',
                    'Best For': '数据存在非线性关系时'
                }
            },
            '【Accuracy Metrics】': {
                'Accuracy@X%': {
                    'Definition': 'X%相对误差范围内的样本比例',
                    'Usage': '反映模型在可接受误差范围内的表现',
                    'Advantage': '与实际应用场景结合紧密',
                    'Best For': '有明确误差容忍度要求时'
                }
            }
        }
        
        for category, metrics in explanations.items():
            print(f"\n{category}")
            print("-" * 70)
            for metric_name, details in metrics.items():
                print(f"\n  {metric_name}:")
                for key, value in details.items():
                    print(f"    • {key}: {value}")
        
        print("\n" + "="*70)
        print("\nMetric Selection Guide:")
        print("  1. General regression: Focus on MAE + RMSE + R²")
        print("  2. With outliers: Focus on MedAE + Spearman")
        print("  3. Different scales: Focus on MAPE + MSLE")
        print("  4. Business application: Focus on Accuracy@X% + MaxError")
        print("  5. Model selection: Consider multiple metrics to avoid overfitting")
        print("="*70 + "\n")
    
    def compare_metrics(self, metrics_dict):
        """
        比较多个模型的指标
        
        参数:
            metrics_dict: 字典，格式为 {model_name: metrics}
        """
        print("\n" + "="*70)
        print("Model Performance Comparison".center(70))
        print("="*70)
        
        # 表头
        print(f"\n{'Model':<20} {'MAE':<10} {'RMSE':<10} {'R²':<10} {'MAPE':<10} {'Acc@10%':<10}")
        print("-" * 70)
        
        # 每个模型的指标
        for model_name, metrics in metrics_dict.items():
            print(f"{model_name:<20} "
                  f"{metrics['MAE']:<10.4f} "
                  f"{metrics['RMSE']:<10.4f} "
                  f"{metrics['R²']:<10.4f} "
                  f"{metrics['MAPE']:<10.2f} "
                  f"{metrics['Accuracy@10%']:<10.2f}")
        
        print("="*70 + "\n")
        
        # 找出最佳模型
        best_models = {}
        metric_names = ['MAE', 'RMSE', 'R²', 'MAPE']
        
        for metric_name in metric_names:
            if metric_name == 'R²':
                # R²越大越好
                best_model = max(metrics_dict.items(), key=lambda x: x[1][metric_name])
            else:
                # 其他指标越小越好
                best_model = min(metrics_dict.items(), key=lambda x: x[1][metric_name])
            best_models[metric_name] = best_model[0]
        
        print("Best Model for Each Metric:")
        for metric_name, model_name in best_models.items():
            print(f"  {metric_name}: {model_name}")
        print()


if __name__ == "__main__":
    # 测试评价指标计算
    print("Testing MetricsCalculator...")
    
    calc = MetricsCalculator()
    
    # 生成模拟数据
    np.random.seed(42)
    y_true = np.random.rand(100) * 20 + 10  # [10, 30]
    y_pred = y_true + np.random.randn(100) * 2  # 添加噪声
    
    print("\nTest data:")
    print(f"  Samples: {len(y_true)}")
    print(f"  True range: [{y_true.min():.2f}, {y_true.max():.2f}]")
    print(f"  Pred range: [{y_pred.min():.2f}, {y_pred.max():.2f}]")
    
    # 计算指标
    metrics = calc.calculate_all_metrics(y_true, y_pred)
    calc.print_metrics(metrics, "Test Metrics")
    
    # 打印指标说明
    calc.print_metric_explanations()
    
    # 测试模型对比
    print("\nTesting model comparison...")
    metrics_dict = {
        'Model A': metrics,
        'Model B': calc.calculate_all_metrics(y_true, y_true + np.random.randn(100) * 3),
        'Model C': calc.calculate_all_metrics(y_true, y_true + np.random.randn(100) * 1),
    }
    calc.compare_metrics(metrics_dict)
    
    print("✓ MetricsCalculator test passed!")